import argparse
import glob
import os
import shutil
import sys

import cv2


# ********下文用于将mAP文件夹中的lib目录放到python环境中，便于引用
# 必须要置于文件顶部
def add_path(path):
    if path not in sys.path:
        sys.path.insert(0, path)


currentPath = os.path.dirname(os.path.realpath(__file__))

# Add lib to PYTHONPATH
libPath = os.path.join(currentPath, 'lib')
add_path(libPath)
# ********上文用于将mAP文件夹中的lib目录放到python环境中，便于引用


from BoundingBox import BoundingBox
from BoundingBoxes import BoundingBoxes
from Evaluator import *
from utilFuns import BBFormat


def get_mAP_by_program(det, gt, iouThreshold=0.3, needPrint=False):
    allBoundingBoxes = BoundingBoxes()
    for item in det:
        bb = BoundingBox(
            imageName=item[6],
            classId=item[0],
            classConfidence=float(item[1]),
            x=float(item[2]),
            y=float(item[3]),
            w=float(item[4]),
            h=float(item[5]),
            typeCoordinates=CoordinatesType.Absolute,
            bbType=BBType.Detected,
            format=BBFormat.XYWH,
            imgSize=item[7])
        allBoundingBoxes.addBoundingBox(bb)
    for item in gt:
        bb = BoundingBox(
            imageName=item[5],
            classId=item[0],
            x=float(item[1]),
            y=float(item[2]),
            w=float(item[3]),
            h=float(item[4]),
            typeCoordinates=CoordinatesType.Absolute,
            bbType=BBType.GroundTruth,
            format=BBFormat.XYWH,
            imgSize=item[6])
        allBoundingBoxes.addBoundingBox(bb)
    mAP = cal_mAP(allBoundingBoxes, iouThreshold, needPrint)
    return round(mAP, 4)


def get_mAP_by_dir(det, gt, iouThreshold=0.3, needPrint=False, imgSize=(1920, 1080)):
    allBoundingBoxes, allClasses = getBoundingBoxes(gt, True, BBFormat.XYWH, BBFormat.XYWH, imgSize=imgSize)
    allBoundingBoxes, _ = getBoundingBoxes(det, False, BBFormat.XYWH, BBFormat.XYWH, allBoundingBoxes, allClasses, imgSize=imgSize)
    mAP = cal_mAP(allBoundingBoxes, iouThreshold, needPrint)
    return mAP


def getBoundingBoxes(directory,
                     isGT,
                     bbFormat,
                     coordType,
                     allBoundingBoxes=None,
                     allClasses=None,
                     imgSize=(0, 0)):
    """Read txt files containing bounding boxes (ground truth and detections)."""
    if allBoundingBoxes is None:
        allBoundingBoxes = BoundingBoxes()
    if allClasses is None:
        allClasses = []
    # Read ground truths
    os.chdir(directory)
    files = glob.glob("*.txt")
    files.sort()


    for f in files:
        nameOfImage = f.replace(".txt", "")
        fh1 = open(f, "r")
        for line in fh1:
            line = line.replace("\n", "")
            if line.replace(' ', '') == '':
                continue
            splitLine = line.split(" ")
            if isGT:
                # idClass = int(splitLine[0]) #class
                idClass = (splitLine[0])  # class
                x = float(splitLine[1])
                y = float(splitLine[2])
                w = float(splitLine[3])
                h = float(splitLine[4])
                bb = BoundingBox(nameOfImage,
                                 idClass,
                                 x,
                                 y,
                                 w,
                                 h,
                                 coordType,
                                 imgSize,
                                 BBType.GroundTruth,
                                 format=bbFormat)
            else:
                # idClass = int(splitLine[0]) #class
                idClass = (splitLine[0])  # class
                if idClass not in allClasses:
                    continue
                confidence = float(splitLine[1])
                x = float(splitLine[2])
                y = float(splitLine[3])
                w = float(splitLine[4])
                h = float(splitLine[5])
                bb = BoundingBox(nameOfImage,
                                 idClass,
                                 x,
                                 y,
                                 w,
                                 h,
                                 coordType,
                                 imgSize,
                                 BBType.Detected,
                                 confidence,
                                 format=bbFormat)
            allBoundingBoxes.addBoundingBox(bb)
            if isGT and (idClass not in allClasses):
                allClasses.append(idClass)
        fh1.close()
    return allBoundingBoxes, allClasses


def cal_mAP(allBoundingBoxes, iouThreshold, needPrint):
    evaluator = Evaluator()

    acc_AP = 0
    validClasses = 0

    # Plot Precision x Recall curve
    detections = evaluator.PlotPrecisionRecallCurve(
        allBoundingBoxes,  # Object containing all bounding boxes (ground truths and detections)
        IOUThreshold=iouThreshold,  # IOU threshold
        method=MethodAveragePrecision.EveryPointInterpolation,
        showAP=False,  # Show Average Precision in the title of the plot
        showInterpolatedPrecision=False,  # Don't plot the interpolated precision curve
        savePath=None,
        showGraphic=False)

    # each detection is a class
    for metricsPerClass in detections:

        # Get metric values per each class
        cl = metricsPerClass['class']
        ap = metricsPerClass['AP']
        precision = metricsPerClass['precision']
        recall = metricsPerClass['recall']
        totalPositives = metricsPerClass['total positives']
        total_TP = metricsPerClass['total TP']
        total_FP = metricsPerClass['total FP']

        if totalPositives > 0:
            validClasses = validClasses + 1
            acc_AP = acc_AP + ap
            prec = ['%.2f' % p for p in precision]
            rec = ['%.2f' % r for r in recall]
            ap_str = "{0:.4f}%".format(ap * 100)
            if needPrint:
                print('AP: %s (%s)' % (ap_str, cl))

    mAP = acc_AP / validClasses
    mAP_str = "{0:.4f}%".format(mAP * 100)
    if needPrint:
        print('mAP: %s' % mAP_str)
    return mAP
